package com.zhang.core;

import com.zhang.constant.Constant;
import com.zhang.util.FileUtils;
import com.zhang.util.HttpUtils;
import com.zhang.util.LogUtils;

import javax.swing.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.concurrent.*;

public class Downloader {
    /**
     * 创建只有一个线程的线程池
     */
    public ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

    /**
     * 创建可见线程变量
     */

    /**
     * 线程池
     */
    public ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            5,
            5,
            0,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(5),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardPolicy());
    /**
     * 计数器对象
     */
    private CountDownLatch countDownLatch = new CountDownLatch(Constant.THREAD_NUM);


    public void download(String url) throws IOException {
        //获取文件名
        String httpFileName = HttpUtils.getHttpFileName(url);
        //拼接路径
        httpFileName = Constant.PATH + httpFileName;

        //获取本地文件的大小
        long localFileLength = FileUtils.getFileContentLength(httpFileName);

        DownloadInfoThread downloadInfoThread = null;

        //获取链接对象
        HttpURLConnection httpUrlConnection = null;
        try {
            httpUrlConnection  = HttpUtils.getHttpUrlConnection(url);

            //获取文件的总大小
            int contentLength = httpUrlConnection.getContentLength();

            //判断是否下载完成
            if (localFileLength >= contentLength){
                LogUtils.info("已经下载完毕的文件: {}",httpFileName);
                return;
            }

            //启动下载信息线程
            downloadInfoThread = new DownloadInfoThread(contentLength);
            //延迟一秒 每隔一秒执行一次
            scheduledExecutorService.scheduleWithFixedDelay(downloadInfoThread,1,1, TimeUnit.SECONDS);

        } catch (IOException e) {
            e.getStackTrace();
        }
        //获取输入流
        try /*(
               *//* // 单线程下载
                InputStream inputStream = httpUrlConnection.getInputStream();
                //缓冲输入
                BufferedInputStream bis =  new BufferedInputStream(inputStream);
                //文件输出流
                FileOutputStream fileOutputStream = new FileOutputStream(httpFileName);
                //输出缓冲
                BufferedOutputStream bos = new BufferedOutputStream(fileOutputStream);*//*
          // =====================================================================================
                //

        )*/ {
            //单线程写入
           /* //写入
            int length = -1;
            byte[]  buffByte = new byte[Constant.BYTE_SIZE];
            while ((length = bis.read(buffByte)) != -1){
                downloadInfoThread.downSize += length;
                bos.write(buffByte,0,length);
            }*/

            //多线程下载 ===================================================
            ArrayList<Future> futureArrayList = new ArrayList<>();
            split(url, futureArrayList);

            //计数器阻塞
            countDownLatch.await();
            /*futureArrayList.forEach(future -> {
                try {
                    //获取线程（下载）结果， 当前线程受阻
                    future.get();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                } catch (ExecutionException e) {
                    throw new RuntimeException(e);
                }

            });*/

        }
        /*} catch (FileNotFoundException e) {
            LogUtils.error("文件不存在:{}", url);
        }*/catch (Exception e){
            LogUtils.error("下载失败:{}", url);
        }finally {
            scheduledExecutorService.shutdownNow();
            if (httpUrlConnection != null) {
                //关闭链接
                httpUrlConnection.disconnect();
            }
            threadPoolExecutor.shutdown();
            System.out.println("下载完成请等待文件合并······");
            fileMerge(url);

        }
    }

    /**
     * 任务分片提交线程池
     * @param url
     */
    public void split(String url, ArrayList<Future> arrayList){
        //获取文件的大小
        try {
            //计算分片大小
            long httpFileConstantLength = HttpUtils.getHttpFileConstantLength(url);
            long size  = httpFileConstantLength / Constant.THREAD_NUM;


            long  startPos;
            long endPos = 0;
            for (int i = 0; i < Constant.THREAD_NUM ; i++) {
                startPos = i * size;

                if (i == Constant.THREAD_NUM - 1) {
                    //最后一块
                    endPos = 0;
                }else {
                    endPos = (i + 1) * size - 1;
                }
                //创建分片下载任务
                DownloadTask downloadTask = new DownloadTask(url, startPos, endPos, i,countDownLatch);
                //提交线程池
                Future<Boolean> future = threadPoolExecutor.submit(downloadTask);
                arrayList.add(future);

            }
        } catch (IOException e) {
           e.getStackTrace();
        }

    }

    /**
     * 分片文件合并
     * @param url
     */
    public void fileMerge(String url) throws IOException {
        String fileName = HttpUtils.getHttpFileName(url);
        String filePath = Constant.PATH + fileName;
        for (int i = 0; i < Constant.THREAD_NUM; i++) {
            LogUtils.info("尝试写入块{}", i);

            File file = new File(Constant.PATH + fileName + ".temp" + i);

            try (       //获取输入流
                        FileInputStream fis = new FileInputStream(file);
                        BufferedInputStream bis = new BufferedInputStream(fis);
                        FileOutputStream fos = new FileOutputStream(filePath, true);
                        BufferedOutputStream bos = new BufferedOutputStream(fos);
            ){
                int length = -1 ;
                byte[] buffer = new byte[Constant.BYTE_SIZE];
                while ((length = bis.read(buffer)) != -1) {
                    bos.write(buffer,0, length);
                }
            } catch (IOException e) {
                LogUtils.error("发生未知错误");
                e.printStackTrace();
            }
            LogUtils.info("成功写入块{}", i);
        }
        //删除所有临时文件
        delTempFile(filePath);
        LogUtils.info("文件合并完成{}", filePath);

    }

    public void delTempFile(String filePath) {
        boolean result = false;
        String tempFile = filePath + ".temp";
        for (int i = 0; i < Constant.THREAD_NUM; i++) {
            File file = new File(tempFile + i);
            if (file.exists()){
                file.delete();
            }
        }
    }
}

